#pragma once

#include "MLEvaluation.h"

#include "Params.h"

Mat MLEvaluation::confusionMatrix(Mat& data, Mat& labels, CvSVMParams& params, const int k)
{
	Mat confusion;
	confusion = Mat::zeros(2,2, CV_32S);
	int testNum = (data.rows / k);
	int init = 0;
	int final = testNum;;
	int cntTrain = 0;
	int cntTest = 0;
	for (int i = 0; i < k; i++)
	{
		Mat_<float> test(testNum, data.cols);
		Mat_<float> train(data.rows - testNum, data.cols);
		Mat_<float> testLabels(testNum, 1);
		Mat_<float> trainLabels(data.rows - testNum, 1);
		for (int n = 0; n < data.rows; n++)
		{
			Mat tempData = data.row(n);
			int tempLabel = labels.at<float>(n,0);
			if (n >= init && n < final )
			{
				test.row(cntTest) = data.row(n) + 0;
				testLabels.at<float>(cntTest,0) = tempLabel;
				cntTest++;
			}
			else
			{

				train.row(cntTrain) = data.row(n) + 0;
				trainLabels.at<float>(cntTrain,0) = tempLabel;
				cntTrain++;
			}
		}
		init = init + testNum;
		final = final + testNum;
		cntTrain = 0;
		cntTest = 0;
		CvSVM svm;
		svm.train(train, trainLabels, Mat(), Mat(), params);
		Mat_<float> predicted(testLabels.rows, 1);
		for(int n = 0; n < test.rows; n++) {
			Mat sample = test.row(n);
			float result = svm.predict(sample);
			predicted.at<float>(n, 0) = result;
		}
		//cout << "TEST" << endl;
		//cout << testLabels << endl;
		//cout << "PREDICTED" << endl;
		//cout << predicted << endl;
		for(int i = 0; i < testLabels.rows; i++) {
			float p = predicted.at<float>(i,0);
			float a = testLabels.at<float>(i,0);
			if ( fabs(p-a) < FLT_EPSILON && a == 1)
			{
				int temp = confusion.at<int>(0,0);
				confusion.at<int>(0,0) = temp + 1;
			}
			else if ( fabs(p-a)>= FLT_EPSILON && a == 0)
			{
				int temp = confusion.at<int>(0,1);
				confusion.at<int>(0,1) = temp + 1;
			}
			else if ( fabs(p-a)>= FLT_EPSILON && a == 1)
			{
				int temp = confusion.at<int>(1,0);
				confusion.at<int>(1,0) = temp + 1;
			}
			else if ( fabs(p-a) < FLT_EPSILON && a == 0)
			{
				int temp = confusion.at<int>(1,1);
				confusion.at<int>(1,1) = temp + 1;
			}
		}
	}
	return confusion;
}

void MLEvaluation::createConfusionMatrix(Mat& data, Mat& labels, CvSVMParams& params, const int k)
{
	confusion = confusionMatrix(data,labels,params,k);
}

void MLEvaluation::evaluate ()
{
	accur = accuracy(confusion);
	precis = precision(confusion);
	sensib = sensibility(confusion);
	specif = specificity(confusion);
	roc = ROC(confusion);
}

float MLEvaluation::sensibility (Mat& confusion)
{
	int TP = confusion.at<int>(0,0);
	int TN = confusion.at<int>(1,1);
	int FN = confusion.at<int>(0,1);
	int FP = confusion.at<int>(1,0);
	return float(TP) / float(TP + FN);
}

float MLEvaluation::specificity (Mat& confusion)
{
	int TP = confusion.at<int>(0,0);
	int TN = confusion.at<int>(1,1);
	int FN = confusion.at<int>(0,1);
	int FP = confusion.at<int>(1,0);
	return float(TN) / float(TN + FP);
}

float MLEvaluation::accuracy (Mat& confusion)
{
	int TP = confusion.at<int>(0,0);
	int TN = confusion.at<int>(1,1);
	int FN = confusion.at<int>(0,1);
	int FP = confusion.at<int>(1,0);
	return float(TP + TN) / float(TP + FP + FN + TN);
}

float MLEvaluation::precision (Mat& confusion)
{
	int TP = confusion.at<int>(0,0);
	int TN = confusion.at<int>(1,1);
	int FN = confusion.at<int>(0,1);
	int FP = confusion.at<int>(1,0);
	return float(TP) / float(TP + FP);
}



float MLEvaluation::ROC (Mat& conf)
{
	return abs(1 + sensibility(conf) - (1 - specificity(conf)))/2;
}

float MLEvaluation::youden (Mat& confusion)
{
	return sensibility(confusion) + specificity(confusion) - 1;
}